#include "Buffer.h"
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include "Log.h"

// 实例化Buffer
Buffer::Buffer(int size) :_capacity(size), _writePos(0), _readPos(0) {
	_data = (char*)malloc(size);
	memset(_data, 0, size);
}

// 析构Buffer
Buffer::~Buffer() { 
	if (_data != nullptr) { free(_data); }
}

// 使Buffer具有size大小的空间
bool Buffer::extendRoom(ssize_t size) {
	// 1. 未写内存够用 -> 无需扩容
	if (writeableSize() >= size) { return true; }

	// 2. [已读内存+可写内存]合并够用 -> 无需扩容, 但需要将已写内存前移，完成[已读内存、可写内存]的合并
	else if (_readPos + writeableSize() >= size) {
		int readable = readableSize();			       // 得到可读内存的大小
		memcpy(_data, _data + _readPos, readable); // 移动可读内存
		_readPos = 0;
		_writePos = readable;
	}

	// 3. [已读内存+可写内存]合并不够用 -> 扩容
	else {
		void* tmp = realloc(_data, _capacity + size);
		if (tmp == NULL) { return false; } // 扩容失败
		memset(static_cast<char*>(tmp) + _capacity, 0, size);
		_data = static_cast<char*>(tmp);
		_capacity += size;
	}
	return true;
}

// 直接写入size大小的数据
int Buffer::appendString(const char* data, int size) {
	if (data == nullptr || size <= 0) { return -1; }

	// 1. 为追加data准备足够的空间
	int ret = extendRoom(size);
	if (!ret) { return -1; }

	// 2. 数据拷贝
	memcpy(_data + _writePos, data, size);
	_writePos += size;
	return 0;
}

// 直接写入数据
int Buffer::appendString(const char* data) {
	int size = strlen(data);
	return appendString(data, size);
}

// 直接写入数据
int Buffer::appendString(const string data) {
	return appendString(data.c_str(), data.size());
}

// 添加数据头。网络数据 = 数据头 + 有效载荷 -> 解决粘包问题
int Buffer::appendHead(const int length) {
	// 转化为网络序列
	LogDebug("小端的数据头长度: %d", length);
	int len = htonl(length);
	LogDebug("大端的数据头长度: %d", len);

	// 类型转换: int -> string
	string head(reinterpret_cast<char*>(&len), sizeof(int));
	appendString(head);
	return 0;
}

// 添加网络数据。网络数据 = 数据头 + 有效载荷
int Buffer::appendPackage(const string data) {
	appendHead(data.size());
	appendString(data);
	return 0;
}

// 读取套接字的数据
int Buffer::readSockfd(int sockfd) {
	// 1. 初始化iovec数组
	struct iovec vec[2];
	int writeable = writeableSize();
	vec[0].iov_base = _data + _writePos;
	vec[0].iov_len = writeable;
	char* tmpbuf = (char*)malloc(40960); // 申请40K
	vec[1].iov_base = tmpbuf;
	vec[1].iov_len = 40960;

	// 2. 读取数据
	int ret = readv(sockfd, vec, 2);
	if (ret == -1) { return -1; }
	else if (ret <= writeable) { // 所有数据都写入到了vec[0]中
		_writePos += ret;
	}
	else { // 数据写完了vec[0]，需要向vec[1]写入，写入后需要将vec[1]的数据追加到buffer中
		_writePos = _capacity; // buffer已被写满
		appendString(tmpbuf, ret - writeable);
	}
	free(tmpbuf);
	return ret;
}

// 根据\r\n取出一行，找到其在数据块中的位置，返回该位置
char* Buffer::findCRLF() {
	// strstr --> 大字符串中匹配子字符串(遇到\0结束)
	// memmem --> 大数据块中匹配子数据块(需要指定数据块大小)
	char* ptr = static_cast<char*>(memmem(_data + _readPos, readableSize(), "\r\n", 2));
	return ptr;
}

// 发送数据
int Buffer::sendData(int sockfd) {
	// 判断有无数据
	int readable = readableSize();
	if (readable > 0) {
		// 可能会产生"管道破裂"问题 -> 解决方法: 参数4指定为MSG_NOSIGNAL
		/*	原因: 一端关闭，另一端还在写，导致管道破裂，内核会发出SIGPIPE信号给当前进程，
		*	服务器进程收到该信号就立即终止了
		*/
		int count = send(sockfd, _data + _readPos, readable, MSG_NOSIGNAL);
		if (count > 0) {
			_readPos += count;
			usleep(1);
		}
		return count;
	}
	return -1;
}
